﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Windows;
using System.Threading;
using System.Security.Cryptography;
using ScottPlot;

namespace TransEchoViewer
{
    public class IEPESensor
    {
        public const int Baudrate = 1152000;
        public bool Connected { get; private set; }
        public object dataLock = new object();
        public ITransPort Port { get; }
        public List<byte> RxBuf { get; private set; }
        public List<double> DataSeries { get; private set; }
        public IEPESensor()
        {
            //Port = new SerialSensor();
            Port = new UdpSensor();
            RxBuf = new List<byte>();
            DataSeries = new List<double>();
            Task.Factory.StartNew(RecvTask, TaskCreationOptions.LongRunning);
        }

        public void Begin(string port)
        {
            Port.PortName = port;
            Port.BaudRate = Baudrate;
            try
            {
                Port.Open();
                Port.DiscardInBuffer();
                Connected = true;
            }
            catch (Exception ex)
            {
                Connected = false;
                MessageBox.Show(ex.Message);
            }
        }

        public const int windowSize = 1024 * 6;
        public int bufSize = windowSize + 4;

        public enum ParseState
        {
            WaitHead,
            RecvData,
            WaitEnd
        }

        public double AdcToDouble(uint adc_value)
        {
            double Vref = 5.0;
            double Voltage_Step = Vref / (Math.Pow(2, 24) - 1);
            if ((adc_value & 0x800000) != 0)
            {
                adc_value -= 0x1000000;
            }
            double voltage = adc_value * Voltage_Step;
            return voltage;
        }

        public ParseState State = ParseState.WaitHead;
        private int dataCnt = 0;
        private void ParseData()
        {
            if(RxBuf.Count < 4)
            {
                return;
            }
            
            if(State == ParseState.WaitHead)
            {
                int len = RxBuf.Count;
                int head_index = 0;
                for (int i = 0; i < len - 3; i++)
                {
                    if (RxBuf[i + 0] == 0xAA &&
                        RxBuf[i + 1] == 0xAA &&
                        RxBuf[i + 2] == 0xAA &&
                        RxBuf[i + 3] == 0xAA )
                    {
                        State = ParseState.RecvData;
                        head_index = i;

#if PRINT
                        Console.WriteLine("Removing...");
                        for (int j = 0;j<head_index+4;j++)
                        {
                            Console.Write(RxBuf[j].ToString("X02") + " ");
                        }
                        Console.Write('\n');
#endif

                        RxBuf.RemoveRange(0, head_index + 4);
                        dataCnt = 0;
                        break;
                    }
                }
            }
            else if (State == ParseState.RecvData)
            {
                int len = RxBuf.Count;
                int dataGot = 0;
                for (int i = 0; i < len - 3; i += 4)
                {
                    byte b0 = RxBuf[i + 0];
                    byte b1 = RxBuf[i + 1];
                    byte b2 = RxBuf[i + 2];
                    byte b3 = RxBuf[i + 3];
                    byte[] intBytes = new byte[] { b0 ,b3, b2, b1 };
                    int adc_value = BitConverter.ToInt32(intBytes, 0);
                    //double f = AdcToDouble(adc_value);

                    lock (dataLock)
                    {
                        DataSeries.Add(adc_value);
                        if (DataSeries.Count > windowSize)
                        {
                            DataSeries.RemoveAt(0);
                        }
                    }
                    dataCnt++;
                    dataGot += 4;
                    if (dataCnt == windowSize / 4)
                    {
                        State = ParseState.WaitHead;
                        break;
                    }
                }
#if PRINT
                Console.WriteLine("Removing...");
                for (int j = 0; j < dataGot; j++)
                {
                    Console.Write(RxBuf[j].ToString("X02") + " ");
                }
                Console.Write('\n');
#endif

                RxBuf.RemoveRange(0, dataGot);
            }            
            else if(State == ParseState.WaitEnd)
            {
                // 已废弃，没有帧尾了
                if (RxBuf[0] == 0xEE &&
                    RxBuf[1] == 0xEE &&
                    RxBuf[2] == 0xEE &&
                    RxBuf[3] == 0xEE)
                {
                    Console.WriteLine("END FRAME OK");
                }
                else
                {
                    Console.WriteLine("END FRAME ERROR");
                }

                State = ParseState.WaitHead;

#if PRINT
                Console.WriteLine("Removing...");
                for (int j = 0; j < 4; j++)
                {
                    Console.Write(RxBuf[j].ToString("X02") + " ");
                }
                Console.Write('\n');
#endif
                RxBuf.RemoveRange(0, 4);
            }

        }

        public void RecvTask()
        {
            while(true)
            {
                if(!Connected)
                {
                    Thread.Sleep(1);
                    continue;
                }

                try
                {
                    if (Port.BytesToRead >= bufSize)
                    {
                        Console.WriteLine($"BytesToRead: {Port.BytesToRead}");
                        int size = Port.BytesToRead;
                        byte[] buf = new byte[size];
                        Port.Read(buf, 0, buf.Length);
                        RxBuf.AddRange(buf);
                        while (RxBuf.Count >= bufSize)
                        {
                            ParseData();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                
                Thread.Sleep(1);
            }
        }

        internal void Stop()
        {
            Port.Close();
            Connected = false;
        }
    }
}
